package com.medusa.aps.business.modules.modeling.mp.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.medusa.aps.business.model.constant.RedisConstants;
import com.medusa.aps.business.model.enums.EquipmentStatus;
import com.medusa.aps.business.model.pagination.Page;
import com.medusa.aps.business.modules.basic.mp.entity.Workshop;
import com.medusa.aps.business.modules.basic.mp.service.WorkshopService;
import com.medusa.aps.business.modules.modeling.model.input.EquipmentDTO;
import com.medusa.aps.business.modules.modeling.model.input.EquipmentParam;
import com.medusa.aps.business.modules.modeling.mp.entity.Equipment;
import com.medusa.aps.business.modules.modeling.mp.entity.WorkingProcedureEquipment;
import com.medusa.aps.business.modules.modeling.mp.mapper.EquipmentMapper;
import com.medusa.aps.business.modules.modeling.mp.service.EquipmentService;
import com.medusa.aps.business.modules.modeling.mp.service.WorkingProcedureEquipmentService;
import com.medusa.aps.business.util.PercentageScheduleUtils;
import com.medusa.aps.business.common.model.resp.SystemCode;
import com.medusa.aps.business.common.redis.annotation.Redisson;
import com.medusa.aps.business.global.model.exception.GlobalException;
import com.medusa.aps.business.global.model.exception.collector.DistinctValidator;
import com.medusa.aps.business.global.model.exception.collector.ErrorCollector;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备服务实现类
 *
 * @author yuxh
 * @since 2023-08-14 17:37:44
 */
@Service("equipmentService")
public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment> implements EquipmentService {

    @Resource
    private EquipmentService equipmentService;
    @Resource
    private WorkshopService workshopService;

    @Resource
    private WorkingProcedureEquipmentService workingProcedureEquipmentService;

    /**
     * 分页查询设备
     *
     * @param equipmentParam 设备查询参数
     * @return 所有数据
     */
    @Override
    public Page<Equipment> pageEquipment(EquipmentParam equipmentParam) {
        return equipmentService.lambdaQuery()
                .in(CollUtil.isNotEmpty(equipmentParam.getIds()), Equipment::getId, equipmentParam.getIds())
                .like(StringUtils.hasText(equipmentParam.getDeviceName()), Equipment::getDeviceName, equipmentParam.getDeviceName())
                .like(StringUtils.hasText(equipmentParam.getEquipmentNumber()), Equipment::getEquipmentNumber, equipmentParam.getEquipmentNumber())
                .like(StringUtils.hasText(equipmentParam.getCreator()), Equipment::getCreator, equipmentParam.getCreator())
                .like(StringUtils.hasText(equipmentParam.getWorkshopName()), Equipment::getWorkshopName, equipmentParam.getWorkshopName())
                .in(CollUtil.isNotEmpty(equipmentParam.getEquipmentNumbers()), Equipment::getEquipmentNumber, equipmentParam.getEquipmentNumbers())
                .between(equipmentParam.getCreateTime().getStartTime() != null && equipmentParam.getCreateTime().getEndTime() != null,
                        Equipment::getCreateTime,
                        equipmentParam.getCreateTime().getStartTime(),
                        equipmentParam.getCreateTime().getEndTime())
                .orderByDesc(Equipment::getCreateTime)
                .orderByDesc(Equipment::getId)
                .page(equipmentParam);
    }

    /**
     * 批量新增设备
     *
     * @param equipmentCreates 实体对象
     */
    @Override
    @Redisson(name = RedisConstants.EQUIPMENT_LOCK_KEY, waitTime = 1)
    @Redisson(name = RedisConstants.WORKSHOP_LOCK_KEY, waitTime = 1)
    public void saveBatchEquipment(List<EquipmentDTO> equipmentCreates) {
        // 校验设备编码不重复
        Map<Integer, Workshop> workshopMap = validatorEquipmentNumber(equipmentCreates);
        List<Equipment> equipments = equipmentCreates.stream()
                .map(equipmentDTO -> equipmentDTO.newEquipment().setLatestTime(LocalDateTime.now())
                        .setWorkshopCode(workshopMap.get(equipmentDTO.getWorkshopId()).getWorkshopCode())
                        .setWorkshopName(workshopMap.get(equipmentDTO.getWorkshopId()).getWorkshopName())
                )
                .toList();
        equipmentService.saveBatch(equipments);

    }

    private Map<Integer, Workshop> validatorEquipmentNumber(List<EquipmentDTO> equipmentCreates) {
        ErrorCollector errors = DistinctValidator.valid(equipmentCreates, EquipmentDTO::getEquipmentNumber, "设备编码");
        if (errors.hasError()) {
            throw new GlobalException(errors.toString());
        }

        List<String> equipmentNumbers = new ArrayList<>(equipmentCreates.size());
        Set<Integer> workshopIds = new HashSet<>(equipmentCreates.size());
        for (EquipmentDTO equipmentCreate : equipmentCreates) {
            equipmentNumbers.add(equipmentCreate.getEquipmentNumber());
            workshopIds.add(equipmentCreate.getWorkshopId());
        }
        List<Equipment> equipments = equipmentService.lambdaQuery()
                .select(Equipment::getEquipmentNumber)
                .in(Equipment::getEquipmentNumber, equipmentNumbers)
                .list();
        if (CollUtil.isNotEmpty(equipments)) {
            Set<String> equipmentNumberDbs = equipments.stream().map(Equipment::getEquipmentNumber).collect(Collectors.toSet());
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < equipmentNumbers.size(); i++) {
                String equipmentNumber = equipmentNumbers.get(i);
                if (equipmentNumberDbs.contains(equipmentNumber)) {
                    stringBuilder.append(StrUtil.format("第{}行,设备编码【{}】系统已存在", i + 1, equipmentNumber));
                }
            }
            throw new GlobalException(stringBuilder.toString());
        }
        return getWorkShop(workshopIds);
    }

    private Map<Integer, Workshop> getWorkShop(Set<Integer> workshopIds) {
        List<Workshop> workshops = workshopService.lambdaQuery()
                .select(Workshop::getId, Workshop::getWorkshopName, Workshop::getWorkshopCode)
                .in(Workshop::getId, workshopIds)
                .list();
        if (CollectionUtils.isEmpty(workshops) || workshops.size() != workshopIds.size()) {
            throw new GlobalException("您输入的车间不存在");
        }
        return workshops.stream()
                .collect(Collectors.toMap(Workshop::getId, workshop -> workshop));
    }

    /**
     * 修改设备
     *
     * @param equipmentDTO 设备对象
     */
    @Override
    @Redisson(name = RedisConstants.EQUIPMENT_LOCK_KEY, waitTime = 1)
    @Redisson(name = RedisConstants.WORKSHOP_LOCK_KEY, waitTime = 1)
    public void updateEquipment(EquipmentDTO equipmentDTO) {
        Long deviceId = equipmentDTO.getId();
        boolean exists = equipmentService.lambdaQuery()
                .ne(Equipment::getId, deviceId)
                .eq(Equipment::getEquipmentNumber, equipmentDTO.getEquipmentNumber())
                .exists();
        if (exists) {
            throw new GlobalException(StrUtil.format("设备编码：【{}】系统已存在", equipmentDTO.getEquipmentNumber()));
        }
        Workshop workshop = workshopService.lambdaQuery()
                .select(Workshop::getId, Workshop::getWorkshopName, Workshop::getWorkshopCode)
                .eq(Workshop::getId, equipmentDTO.getWorkshopId())
                .one();
        if (workshop == null) {
            throw new GlobalException("车间不存在");
        }
        Equipment equipment = this.getById(deviceId);
        if (equipment == null) {
            throw SystemCode.PARAM_VALID_ERROR.msgEx("设备不存在");
        }
        equipment.setDeviceName(equipmentDTO.getDeviceName())
                .setDeviceType(equipmentDTO.getDeviceType())
                .setEquipmentStatus(equipmentDTO.getEquipmentStatus())
                .setWorkshopId(equipmentDTO.getWorkshopId())
                .setWorkshopName(workshop.getWorkshopName())
                .setWorkshopCode(workshop.getWorkshopCode());
        equipmentService.updateById(equipment);
    }

    /**
     * 删除数据
     *
     * @param ids 设备id
     */
    @Override
    @Redisson(name = RedisConstants.EQUIPMENT_LOCK_KEY, waitTime = 1)
    public void deleteBatch(List<Long> ids) {
        PercentageScheduleUtils.validClassSystemGenerateCalendar();
        List<Equipment> equipments = equipmentService.lambdaQuery()
                .select(Equipment::getDeviceName, Equipment::getEquipmentNumber, Equipment::getEquipmentStatus)
                .in(Equipment::getId, ids)
                .list();
        if (CollUtil.isEmpty(equipments)) {
            throw new GlobalException(SystemCode.DATA_NOT_EXIST.getMsg());
        }
        //校验设备状态
        equipments.stream()
                .filter(item -> item.getEquipmentStatus() == EquipmentStatus.USING)
                .map(Equipment::getEquipmentNumber)
                .reduce((a, b) -> a + StrUtil.COMMA + b)
                .ifPresent(item -> {
                    throw new GlobalException(StrUtil.format("设备【{}】正在使用中，暂时无法删除!", item));
                });
        PercentageScheduleUtils.valid(
                workingProcedureEquipmentService.lambdaQuery()
                        .select(WorkingProcedureEquipment::getEquipmentNumber)
                        .in(WorkingProcedureEquipment::getEquipmentId, ids)
                        .list(),
                WorkingProcedureEquipment::getEquipmentNumber,
                "设备【{}】已被工序引用，暂时无法删除!"
        );
        equipmentService.removeByIds(ids);
    }
}

